/*
* Copyright (C) 1997-2001 Id Software, Inc.
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.
* 
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*  
*/

// Created on 29.12.2003 by RST.
// $Id: GameSave.java,v 1.11 2006/01/21 21:53:32 salomo Exp $
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Com = Suake2.UI.qcommon.Com;
using Lib = Suake2.UI.util.Lib;
using QuakeFile = Suake2.UI.util.QuakeFile;
namespace Suake2.UI.game
{
	
	public class GameSave
	{
		
		public static void  CreateEdicts()
		{
			GameBase.g_edicts = new edict_t[GameBase.game.maxentities];
			for (int i = 0; i < GameBase.game.maxentities; i++)
				GameBase.g_edicts[i] = new edict_t(i);
			GameBase.g_edicts = GameBase.g_edicts;
		}
		
		public static void  CreateClients()
		{
			GameBase.game.clients = new gclient_t[GameBase.game.maxclients];
			for (int i = 0; i < GameBase.game.maxclients; i++)
				GameBase.game.clients[i] = new gclient_t(i);
		}
		
		private static System.String[] preloadclasslist = new System.String[]{"jake2.game.PlayerWeapon", "jake2.game.AIAdapter", "jake2.game.Cmd", "jake2.game.EdictFindFilter", "jake2.game.EdictIterator", "jake2.game.EndianHandler", "jake2.game.EntBlockedAdapter", "jake2.game.EntDieAdapter", "jake2.game.EntDodgeAdapter", "jake2.game.EntInteractAdapter", "jake2.game.EntPainAdapter", "jake2.game.EntThinkAdapter", "jake2.game.EntTouchAdapter", "jake2.game.EntUseAdapter", "jake2.game.GameAI", "jake2.game.GameBase", "jake2.game.GameChase", "jake2.game.GameCombat", "jake2.game.GameFunc", "jake2.game.GameMisc", "jake2.game.GameSVCmds", "jake2.game.GameSave", "jake2.game.GameSpawn", "jake2.game.GameTarget", "jake2.game.GameTrigger", "jake2.game.GameTurret", "jake2.game.GameUtil", "jake2.game.GameWeapon", "jake2.game.Info", "jake2.game.ItemDropAdapter", "jake2.game.ItemUseAdapter", "jake2.game.Monster", "jake2.game.PlayerClient", "jake2.game.PlayerHud", "jake2.game.PlayerTrail", "jake2.game.PlayerView", "jake2.game.SuperAdapter", "jake2.game.monsters.M_Actor", "jake2.game.monsters.M_Berserk", "jake2.game.monsters.M_Boss2", "jake2.game.monsters.M_Boss3", "jake2.game.monsters.M_Boss31", "jake2.game.monsters.M_Boss32", "jake2.game.monsters.M_Brain", "jake2.game.monsters.M_Chick", "jake2.game.monsters.M_Flash", "jake2.game.monsters.M_Flipper", "jake2.game.monsters.M_Float", "jake2.game.monsters.M_Flyer", "jake2.game.monsters.M_Gladiator", "jake2.game.monsters.M_Gunner", "jake2.game.monsters.M_Hover", "jake2.game.monsters.M_Infantry", "jake2.game.monsters.M_Insane", "jake2.game.monsters.M_Medic", "jake2.game.monsters.M_Mutant", "jake2.game.monsters.M_Parasite", "jake2.game.monsters.M_Player", "jake2.game.monsters.M_Soldier", "jake2.game.monsters.M_Supertank", "jake2.game.monsters.M_Tank", "jake2.game.GameItems", "jake2.game.GameItemList"};
		
		/// <summary> InitGame
		/// 
		/// This will be called when the dll is first loaded, which only happens when
		/// a new game is started or a save game is loaded. 
		/// </summary>
		public static void  InitGame()
		{
			GameBase.gi.dprintf("==== InitGame ====\n");
			
			// preload all classes to register the adapters
			for (int n = 0; n < preloadclasslist.Length; n++)
			{
				try
				{
					//UPGRADE_TODO: The differences in the format  of parameters for method 'java.lang.Class.forName'  may cause compilation errors.  "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1092'"
					System.Type.GetType(preloadclasslist[n]);
				}
				catch (System.Exception e)
				{
					//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.getMessage' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
					Com.DPrintf("error loading class: " + e.Message);
				}
			}
			
			
			GameBase.gun_x = GameBase.gi.cvar("gun_x", "0", 0);
			GameBase.gun_y = GameBase.gi.cvar("gun_y", "0", 0);
			GameBase.gun_z = GameBase.gi.cvar("gun_z", "0", 0);
			
			//FIXME: sv_ prefix are wrong names for these variables 
			GameBase.sv_rollspeed = GameBase.gi.cvar("sv_rollspeed", "200", 0);
			GameBase.sv_rollangle = GameBase.gi.cvar("sv_rollangle", "2", 0);
			GameBase.sv_maxvelocity = GameBase.gi.cvar("sv_maxvelocity", "2000", 0);
			GameBase.sv_gravity = GameBase.gi.cvar("sv_gravity", "800", 0);
			
			// noset vars
			Globals.dedicated = GameBase.gi.cvar("dedicated", "0", Defines.CVAR_NOSET);
			
			// latched vars
			GameBase.sv_cheats = GameBase.gi.cvar("cheats", "0", Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
			GameBase.gi.cvar("gamename", Defines.GAMEVERSION, Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
			GameBase.gi.cvar("gamedate", Globals.__DATE__, Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
			
			GameBase.maxclients = GameBase.gi.cvar("maxclients", "4", Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
			GameBase.maxspectators = GameBase.gi.cvar("maxspectators", "4", Defines.CVAR_SERVERINFO);
			GameBase.deathmatch = GameBase.gi.cvar("deathmatch", "0", Defines.CVAR_LATCH);
			GameBase.coop = GameBase.gi.cvar("coop", "0", Defines.CVAR_LATCH);
			GameBase.skill = GameBase.gi.cvar("skill", "0", Defines.CVAR_LATCH);
			GameBase.maxentities = GameBase.gi.cvar("maxentities", "1024", Defines.CVAR_LATCH);
			
			// change anytime vars
			GameBase.dmflags = GameBase.gi.cvar("dmflags", "0", Defines.CVAR_SERVERINFO);
			GameBase.fraglimit = GameBase.gi.cvar("fraglimit", "0", Defines.CVAR_SERVERINFO);
			GameBase.timelimit = GameBase.gi.cvar("timelimit", "0", Defines.CVAR_SERVERINFO);
			GameBase.password = GameBase.gi.cvar("password", "", Defines.CVAR_USERINFO);
			GameBase.spectator_password = GameBase.gi.cvar("spectator_password", "", Defines.CVAR_USERINFO);
			GameBase.needpass = GameBase.gi.cvar("needpass", "0", Defines.CVAR_SERVERINFO);
			GameBase.filterban = GameBase.gi.cvar("filterban", "1", 0);
			
			GameBase.g_select_empty = GameBase.gi.cvar("g_select_empty", "0", Defines.CVAR_ARCHIVE);
			
			GameBase.run_pitch = GameBase.gi.cvar("run_pitch", "0.002", 0);
			GameBase.run_roll = GameBase.gi.cvar("run_roll", "0.005", 0);
			GameBase.bob_up = GameBase.gi.cvar("bob_up", "0.005", 0);
			GameBase.bob_pitch = GameBase.gi.cvar("bob_pitch", "0.002", 0);
			GameBase.bob_roll = GameBase.gi.cvar("bob_roll", "0.002", 0);
			
			// flood control
			GameBase.flood_msgs = GameBase.gi.cvar("flood_msgs", "4", 0);
			GameBase.flood_persecond = GameBase.gi.cvar("flood_persecond", "4", 0);
			GameBase.flood_waitdelay = GameBase.gi.cvar("flood_waitdelay", "10", 0);
			
			// dm map list
			GameBase.sv_maplist = GameBase.gi.cvar("sv_maplist", "", 0);
			
			// items
			GameItems.InitItems();
			
			GameBase.game.helpmessage1 = "";
			GameBase.game.helpmessage2 = "";
			
			// initialize all entities for this game
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			GameBase.game.maxentities = (int) GameBase.maxentities.value_Renamed;
			CreateEdicts();
			
			// initialize all clients for this game
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			GameBase.game.maxclients = (int) GameBase.maxclients.value_Renamed;
			
			CreateClients();
			
			GameBase.num_edicts = GameBase.game.maxclients + 1;
		}
		
		/// <summary> WriteGame
		/// 
		/// This will be called whenever the game goes to a new level, and when the
		/// user explicitly saves the game.
		/// 
		/// Game information include cross level data, like multi level triggers,
		/// help computer info, and all client states.
		/// 
		/// A single player death will automatically restore from the last save
		/// position.
		/// </summary>
		public static void  WriteGame(System.String filename, bool autosave)
		{
			try
			{
				QuakeFile f;
				
				if (!autosave)
					PlayerClient.SaveClientData();
				
				f = new QuakeFile(filename, "rw");
				
				if (f == null)
					GameBase.gi.error("Couldn't write to " + filename);
				
				GameBase.game.autosaved = autosave;
				GameBase.game.write(f);
				GameBase.game.autosaved = false;
				
				for (int i = 0; i < GameBase.game.maxclients; i++)
					GameBase.game.clients[i].write(f);
				
				Lib.fclose(f);
			}
			catch (System.Exception e)
			{
				SupportClass.WriteStackTrace(e, Console.Error);
			}
		}
		
		public static void  ReadGame(System.String filename)
		{
			
			QuakeFile f = null;
			
			try
			{
				
				f = new QuakeFile(filename, "r");
				CreateEdicts();
				
				GameBase.game.load(f);
				
				for (int i = 0; i < GameBase.game.maxclients; i++)
				{
					GameBase.game.clients[i] = new gclient_t(i);
					GameBase.game.clients[i].read(f);
				}
				
				f.Close();
			}
			catch (System.Exception e)
			{
				SupportClass.WriteStackTrace(e, Console.Error);
			}
		}
		
		/// <summary> WriteLevel</summary>
		public static void  WriteLevel(System.String filename)
		{
			try
			{
				int i;
				edict_t ent;
				QuakeFile f;
				
				f = new QuakeFile(filename, "rw");
				if (f == null)
					GameBase.gi.error("Couldn't open for writing: " + filename);
				
				// write out level_locals_t
				GameBase.level.write(f);
				
				// write out all the entities
				for (i = 0; i < GameBase.num_edicts; i++)
				{
					ent = GameBase.g_edicts[i];
					if (!ent.inuse)
						continue;
					System.IO.BinaryWriter temp_BinaryWriter;
					temp_BinaryWriter = new System.IO.BinaryWriter(f);
					temp_BinaryWriter.Write((System.Int32) i);
					ent.write(f);
				}
				
				i = - 1;
				System.IO.BinaryWriter temp_BinaryWriter2;
				temp_BinaryWriter2 = new System.IO.BinaryWriter(f);
				temp_BinaryWriter2.Write((System.Int32) (- 1));
				
				f.Close();
			}
			catch (System.Exception e)
			{
				SupportClass.WriteStackTrace(e, Console.Error);
			}
		}
		
		/// <summary> ReadLevel
		/// 
		/// SpawnEntities will allready have been called on the level the same way it
		/// was when the level was saved.
		/// 
		/// That is necessary to get the baselines set up identically.
		/// 
		/// The server will have cleared all of the world links before calling
		/// ReadLevel.
		/// 
		/// No clients are connected yet.
		/// </summary>
		public static void  ReadLevel(System.String filename)
		{
			try
			{
				edict_t ent;
				
				QuakeFile f = new QuakeFile(filename, "r");
				
				if (f == null)
					GameBase.gi.error("Couldn't read level file " + filename);
				
				// wipe all the entities
				CreateEdicts();
				
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				GameBase.num_edicts = (int) GameBase.maxclients.value_Renamed + 1;
				
				// load the level locals
				GameBase.level.read(f);
				
				// load all the entities
				while (true)
				{
					System.IO.BinaryReader temp_BinaryReader;
					temp_BinaryReader = new System.IO.BinaryReader(f);
					temp_BinaryReader.BaseStream.Position = f.Position;
					int entnum = temp_BinaryReader.ReadInt32();
					if (entnum == - 1)
						break;
					
					if (entnum >= GameBase.num_edicts)
						GameBase.num_edicts = entnum + 1;
					
					ent = GameBase.g_edicts[entnum];
					ent.read(f);
					ent.cleararealinks();
					GameBase.gi.linkentity(ent);
				}
				
				Lib.fclose(f);
				
				// mark all clients as unconnected
				for (int i = 0; i < GameBase.maxclients.value_Renamed; i++)
				{
					ent = GameBase.g_edicts[i + 1];
					ent.client = GameBase.game.clients[i];
					ent.client.pers.connected = false;
				}
				
				// do any load time things at this point
				for (int i = 0; i < GameBase.num_edicts; i++)
				{
					ent = GameBase.g_edicts[i];
					
					if (!ent.inuse)
						continue;
					
					// fire any cross-level triggers
					if (ent.classname != null)
						if (Lib.strcmp(ent.classname, "target_crosslevel_target") == 0)
							ent.nextthink = GameBase.level.time + ent.delay;
				}
			}
			catch (System.Exception e)
			{
				SupportClass.WriteStackTrace(e, Console.Error);
			}
		}
	}
}